#pragma once

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <time.h>
#include <sys/time.h>
#include <iostream>
#include <errno.h>
#include "ICHAT_PacketBase.h"
using namespace std;

enum MEMDBPACKETVER
{
    MEMDBSERVER_PACEKTVER = 1,
    MEMDBSERVER_SUBPACKETVER = 2
};

enum WMODE
{
    WMODE_GAME_PLAY_WIN = 487,  //玩牌赢（获得）
    WMODE_GAME_PLAY_LOSE = 488, //玩牌输（消费）
    WMODE_TARNEEB_ROBOT_MONEY_INIT = 1492,
    WMODE_TARNEEB_ROBOT_MONEY_MAX = 1493,

    WMODE_TARNEEB_ROBOT_ADDMONEY = 1521,
    WMODE_TARNEEB_ROBOT_REDUCEMONEY = 1522,

};

enum
{   
    /////
    //SERVER_AND_CLIENT_MAX_COMMAND
    // 客户端消息
    CLIENT_COMMAND_CREATE_RECORD = 0x1001,    //创建用户
    CLIENT_COMMAND_UPDATE_RECORD,            //更新用户钱数,经验值
    CLIENT_COMMAND_DELETE_RECORD,            //删除用户
    CLIENT_COMMAND_GET_RECORD,                //取用户钱数,经验值
    CLIENT_COMMAND_REPORT_ID,                //上报身份, 1为client, 2为备份server
    CLIENT_COMMAND_GET_ALLRECORD,            //取所有用户数据
    CLIENT_COMMAND_GET_UPDATEALLRECORD,        //取所有需更新用户数据
    CLIENT_COMMAND_SWITCH_LOG,                //写日志开关
    CLIENT_COMMAND_SIT_RECORD,            //到平台方获取买入的金钱
    CLIENT_COMMAND_STAND_RECORD,            //回写到平台方买入的金钱
    
    //服务器回应消息
    SERVER_COMMAND_CREATE_RECORD = 0x2001,    //服务器回应创建用户
    SERVER_COMMAND_UPDATE_RECORD,            //服务器回应更新用户
    SERVER_COMMAND_DELETE_RECORD,            //服务器回应删除用户
    SERVER_COMMAND_GET_RECORD,                //服务器回应取用户
    SERVER_COMMAND_GET_ALLRECORD,            //服务器回应所有用户数据
    SERVER_COMMAND_GET_UPDATEALLRECORD,        //返回所有需更新用户数据
    SERVER_COMMAND_SIT_RECORD,            //到平台方获取买入的金钱
    SERVER_COMMAND_STAND_RECORD,            //回写到平台方买入的金钱

    //FORWARD_COMMAND_SET_RECORD = 0x3001,    //转发设置数据 
    /////
    //SERVER_AND_CLIENT_MAX_COMMAND
    //CasServer
    // 客户端消息
    CLIENT_COMMAND_GET_UPDATE_RECORD = 0x5001,        //取并更新数据

    //服务器回应消息
    SERVER_COMMAND_GET_UPDATE_RECORD = 0x6001,        //返回更新数据
};

#define ICHAT_TCP_DEFAULT_BUFFER    1024*64  
typedef InputPacket<ICHAT_TCP_DEFAULT_BUFFER, 9>    MemdbInputPacket;
typedef OutputPacket<ICHAT_TCP_DEFAULT_BUFFER, 9>    MemdbOutputPacket;

class CMemdbClient
{
public:
    bool ConnectMemdb(const char* ip, int port);
    int Recv(void);
    int Send(void* buff, int sz);
    //int SendAndRecv(void* buff, int sz);
    string GetUserData(int playerId);
    int UpdateUserMoney(int playerId, long long money);
    int TarneebResetUserMoney(int playerId, long long money, int wmode);

public:
    char m_recvBuf[ICHAT_TCP_DEFAULT_BUFFER];

private:
    struct timeval  m_tv;
    int m_fd;
    long long m_test_count;
};

bool CMemdbClient::ConnectMemdb(const char* ip, int port)
{
    m_tv.tv_sec = 2;
    m_tv.tv_usec = 0;
    m_test_count = 0;
    m_fd = socket(AF_INET, SOCK_STREAM, 0);

    setsockopt(m_fd, SOL_SOCKET, SO_RCVTIMEO, &m_tv, sizeof(m_tv));
    setsockopt(m_fd, SOL_SOCKET, SO_SNDTIMEO, &m_tv, sizeof(m_tv));

    //keepalive
    int keepalive = 1;
    setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepalive, sizeof(keepalive));
    int keepidle = 60;      // 首次探测开始前的空闲时间
    int keepintvl = 60;     // 两次探测之间的时间间隔
    int keepcnt = 6;        // 最大重试次数
    setsockopt(m_fd, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&keepidle, sizeof(keepidle));
    setsockopt(m_fd, IPPROTO_TCP, TCP_KEEPINTVL, (void*)&keepintvl, sizeof(keepintvl));
    setsockopt(m_fd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&keepcnt, sizeof(keepcnt));

    sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ip);
    
    int ret = connect(m_fd, (sockaddr*)&server, sizeof(server));
    if (ret < 0) {
        //printf("connect MServer falied!\n");
        return -1;
    }

    //printf("connect MServer ok! fd=%d\n", m_fd);
    return 0;
}

int CMemdbClient::Send(void* buff, int sz)
{
    return send(m_fd, (char*)buff, sz, 0);
}

int CMemdbClient::Recv(void)
{
    int ret = recv(m_fd, m_recvBuf, ICHAT_TCP_DEFAULT_BUFFER, 0);
    m_recvBuf[ret] = 0;
    
    return ret;
}

string CMemdbClient::GetUserData(int playerId)
{
    //this->m_test_count += 1;
    //printf("--------------test_count: %ld----------\n", this->m_test_count);

    MemdbOutputPacket response;
    response.Begin(CLIENT_COMMAND_GET_RECORD, MEMDBSERVER_PACEKTVER, MEMDBSERVER_SUBPACKETVER);
    response.WriteInt(playerId);
    response.End();
    response.EncryptBuffer();

    int iRet = this->Send(response.packet_buf(), response.packet_size());
    iRet = this->Recv();
    if (iRet<0) {
        //printf("socket disconne!\n");
        return "";
    }
    
    MemdbInputPacket pack;
    pack.Copy(this->m_recvBuf, iRet);
    iRet = pack.DecryptBuffer();
    if (iRet == -1) {
        //printf("DecryptBuffer=-1!\n");
        return "";
    }
    //printf("DecryptBuffer iRet=%d!\n", iRet);

    short nCmdType = pack.GetCmdType();
    //printf("nCmdType=%d!\n", nCmdType);

    iRet = pack.ReadInt();
    std::string str = pack.ReadString();
    //printf("ReadString c_str=%s\n", str.c_str());

    //struct timeval tv;
    //gettimeofday(&tv, NULL);
    //printf("--------------time: %ld----------\n", tv.tv_sec); 

    return str.c_str();
}

int CMemdbClient::UpdateUserMoney(int playerId, long long money)
{
    char szData[512] = {0};
    sprintf(szData, "money:%ld,wmode:%d,addmoney:%ld", money, WMODE_TARNEEB_ROBOT_MONEY_MAX, money);

    MemdbOutputPacket response;
    response.Begin(CLIENT_COMMAND_STAND_RECORD, MEMDBSERVER_PACEKTVER, MEMDBSERVER_SUBPACKETVER);
    response.WriteInt(playerId);
    response.WriteString(szData);
    response.End();
    response.EncryptBuffer();

    int iRet = this->Send(response.packet_buf(), response.packet_size());
    iRet = this->Recv();
    if (iRet<0) {
        //printf("socket disconne!\n");
        return -1;
    }

    MemdbInputPacket pack;
    pack.Copy(this->m_recvBuf, iRet);
    iRet = pack.DecryptBuffer();
    if (iRet == -1) {
        //printf("DecryptBuffer=-1!\n");
        return -1;
    }

    return 0;
}

int CMemdbClient::TarneebResetUserMoney(int playerId, long long money, int wmode)
{
    char szData[512] = {0};
    sprintf(szData, "money:%ld,wmode:%d,addmoney:%ld", money, wmode, money);

    MemdbOutputPacket response;
    response.Begin(CLIENT_COMMAND_STAND_RECORD, MEMDBSERVER_PACEKTVER, MEMDBSERVER_SUBPACKETVER);
    response.WriteInt(playerId);
    response.WriteString(szData);
    response.End();
    response.EncryptBuffer();

    int iRet = this->Send(response.packet_buf(), response.packet_size());
    iRet = this->Recv();
    if (iRet<0) {
        //printf("socket disconne!\n");
        return -1;
    }

    MemdbInputPacket pack;
    pack.Copy(this->m_recvBuf, iRet);
    iRet = pack.DecryptBuffer();
    if (iRet == -1) {
        //printf("DecryptBuffer=-1!\n");
        return -1;
    }

    return 0;
}

int main()
{   
    MemdbOutputPacket response;
    response.Begin(CLIENT_COMMAND_GET_RECORD, MEMDBSERVER_PACEKTVER, MEMDBSERVER_SUBPACKETVER);
    response.WriteInt(215698);
    response.End();
    response.EncryptBuffer();

    CMemdbClient* c = new CMemdbClient;
    int ret1 = c->ConnectMemdb("192.168.202.92", 12515);
    while (true) {
        int iRet = c->Send(response.packet_buf(), response.packet_size());
        int iRet2 = c->Recv();
        if (iRet2<0) {
            printf("socket disconne!\n");
            return 0;
        }

        MemdbInputPacket pack;
        pack.Copy(c->m_recvBuf, iRet2);
        int k = pack.DecryptBuffer();
        if ( k== -1) {
            printf("DecryptBuffer=-1!\n");
            return -1;
        }
        printf("k=%d!\n", k);

        short nCmdType = pack.GetCmdType();
        printf("nCmdType=%d!\n", nCmdType);
        int iret = pack.ReadInt();
        std::string str = pack.ReadString();
        printf("str=%s", str.c_str());

        struct timeval tv;
        gettimeofday(&tv, NULL);
        printf("second: %ld\n", tv.tv_sec); 
    }

    delete c;

    return 0;
}

